﻿#region copyright info
//------------------------------------------------------------------------------
// <copyright company="ChaosStudio">
//  Copyright (c) 2002-2010 巧思工作室.  All rights reserved.
//  Contact:		MSN:zhouyu@cszi.com , QQ:478779122
//	  Link:				http://www.69sea.com http://www.cszi.com
// </copyright>
//------------------------------------------------------------------------------
#endregion

using System;
using System.Collections;
using System.Web.Caching;

namespace CS.Caching
{
    /// <summary>
    ///   CsCache，简单的Cache实现
    ///   v:0.1.1
    /// </summary>
    /// 
    /// <description class = "CS.Cache.CsCache">
    ///   被动模式下会先耗光缓存设定的数量再进行移去过期检查。主动模式下每次获取缓存内容时都全面检查。
    ///   <para>Note:主动模式按缓存创建时间过期，被动模式按缓存访问时间过期</para>
    /// </description>
    /// 
    /// <history>
    ///   2010-3-22 14:23:37 , zhouyu ,  创建	
    ///   2010-6-24 zhouyu 加入缓存依赖项，可以依赖一个具体的文件，缺点，文件里有关联时并不能依赖关联。     
    ///  </history>
    public class CsCache<TKey, TValue>
    {
        /// <summary>
        /// 缓存生存时间(分钟)
        /// </summary>
        public double LifeTime { get; set; }

        /// <summary>
        /// 最大缓存数量，超过自动删除使用次数较少的对象
        /// </summary>
        public int MaxCache { get; set; }

        /// <summary>
        /// 缓存名字
        /// </summary>
        public string CacheName { get; set; }

        /// <summary>
        /// 缓存数量
        /// </summary>
        public int Caches
        {
            get { return CacheTable.Count; }
        }

        /// <summary>
        /// 缓存过期模式
        /// </summary>
        public ExpireModeType ExpireMode { get; set; }

       
        /// <summary>
        /// 本地缓存名字
        /// </summary>
        public string LocalCacheName { get; set; }
        protected Hashtable CacheTable;
        /// <summary>
        /// 同步锁
        /// </summary>
        public object SyncRoot;

        /// <summary>
        /// 默认构造
        /// Note:Passive下如果缓存在有效期内一直使用则永不过期
        /// </summary>
        public CsCache()
            : this(10, 128, ExpireModeType.Passive)
        {
        }

        /// <summary>
        /// 初始化构造
        /// </summary>
        /// <param name="lifeTime">生命周期，分钟</param>
        /// <param name="maxCache">最大缓存数量</param>
        /// <param name="expireMode">过期模式</param>
        
        public CsCache(double lifeTime, int maxCache, ExpireModeType expireMode)
        {
            LifeTime = lifeTime;
            CacheName = "CS_Cache_";
            MaxCache = maxCache;
            CacheTable = new Hashtable();
            SyncRoot = CacheTable.SyncRoot;
            ExpireMode = expireMode;  //该模式下如果缓存在有效期内一直使用则永不过期
            //Dependency = dependency;
        }

        /// <summary>
        /// 通过缓存索引获取值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public virtual TValue this[TKey key]
        {
            get
            {
                TValue t;
                //var rst = TryGet(key, out t);
                TryGet(key, out t);
                return t;
            }
            set { Add(key, value); }
        }

        /// <summary>
        /// 获取缓存中的值
        /// </summary>
        /// <param name="key">缓存的键</param>
        /// <returns>未找到缓存时为default(T)，否则返回正确的结果</returns>
        public virtual TValue Get(TKey key)
        {
            TValue rst;
            TryGet(key, out rst);
            return rst;
        }

        /// <summary>
        /// 获取缓存内容
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="value">试图获取的值</param>
        /// <returns>是否获取成功</returns>
        public virtual bool TryGet(TKey key, out TValue value)
        {
            RemoveExpireCache();    //移除过期的缓存
            if (CacheTable.ContainsKey(key))
            {
                var bag = ((CacheBag<TKey, TValue>)CacheTable[key]);
                if (bag == null)
                {
                    value = default(TValue);
                    return false;
                }

                //依赖变动
                if(bag.Dependency.HasChanged)
                {
                    //Remove(key);
                    value = default(TValue);
                    return false;
                }

                var now = DateTime.Now;

                //主动过期模式按创建时间删除缓存
                if (ExpireMode == ExpireModeType.Active)
                {
                    if (bag.CreateTime.AddMinutes(LifeTime) < now)
                    {
                        Remove(key);
                        value = default(TValue);  //过期删除
                        return false;
                    }
                    value = bag.Value;
                    bag.AccessTime = now;
                    bag.Hits++;
                    return true;
                }

                //被动模式且最后访问时间未过期时返回缓存
                if (bag.AccessTime.AddMinutes(LifeTime) >= DateTime.Now)
                {
                    value = bag.Value;
                    bag.AccessTime = now;
                    bag.Hits++;
                    return true;
                }

                //移去过期的缓存
                Remove(key);
                value = default(TValue);
                return false;
            }
            value = default(TValue);
            return false;
        }

        public virtual void Add(TKey key,TValue value)
        {
            Add(key,value,null);
        }
        /// <summary>
        /// 把对象按指定的键值保存到缓存中
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="value">保存的对象</param>
        /// <param name="dependency">缓存的依赖项</param>
        public virtual void Add(TKey key, TValue value,CacheDependency dependency)
        {
            //var saveHandler = new EventSaveCacheHandler(SetCache);
            //var iar = saveHandler.BeginInvoke(key, value, Results, null);
            SetCache(key, value,dependency);
        }

        /// <summary>
        /// 把对象按指定的键值保存到缓存中
        /// </summary>
        /// <param name="key">键值</param>
        /// <param name="value">保存的对象</param>
        /// <param name="dependency">缓存的依赖项</param>
        protected virtual void SetCache(TKey key, TValue value,CacheDependency dependency)
        {
            RemoveMinHitCache();    //如果需要移去很少用到的缓存
            lock (SyncRoot)
            {
                CacheTable[key] = new CacheBag<TKey, TValue>(key, value,dependency);
            }
        }

        /// <summary>
        /// 主动模式时(且缓存数量不超过最大值) 按访问时间过期移除缓存
        /// </summary>
        protected virtual void RemoveExpireCache()
        {
            //被动模式时缓存不满不清除
            if (Caches < MaxCache && ExpireMode == ExpireModeType.Passive) return;
            var now = DateTime.Now;
            foreach (var row in CacheTable.Values)
            {
                var t = (CacheBag<TKey, TValue>)row;
                if (t.AccessTime.AddMinutes(LifeTime) < now)
                {
                    Remove(t.Key);
                }
            }
        }

        /// <summary>
        /// 超过设定缓存时 移去很少用到的Cache
        /// </summary>
        protected virtual void RemoveMinHitCache()
        {
            if (Caches < MaxCache) return;
            //查找很少用到的Cache并移除
            var min = 0;
            var key = default(TKey);
            foreach (var row in CacheTable.Values)
            {
                var t = (CacheBag<TKey, TValue>)row;
                if (min >= t.Hits) continue;
                key = t.Key;
                min = t.Hits;
            }
            Remove(key);
        }

        /// <summary>
        /// 在缓存中删除指定键值的对象
        /// </summary>
        /// <param name="key">键值</param>
        public virtual void Remove(TKey key)
        {
            lock (SyncRoot)
            {
                CacheTable.Remove(key);
            }
        }

        /// <summary>
        /// 清除缓存中所有对象
        /// </summary>
        public virtual void Clear()
        {
            lock (SyncRoot)
            {
                CacheTable.Clear();
            }
        }



        #region 缓存包装器

        /// <summary>
        /// 缓存内容的再包装
        /// </summary>
        /// <typeparam name="TDKey">缓存内容的键</typeparam>
        /// <typeparam name="TData">缓存内容</typeparam>
        [Serializable]
        protected class CacheBag<TDKey, TData>
        {
            /// <summary>
            /// 缓存内容的键
            /// </summary>
            public TDKey Key { get; set; }
            /// <summary>
            /// 缓存内容
            /// </summary>
            public TData Value { get; set; }

            /// <summary>
            /// 创建的时间
            /// </summary>
            public DateTime CreateTime { get; set; }

            /// <summary>
            /// 访问的时间
            /// </summary>
            public DateTime AccessTime { get; set; }

            /// <summary>
            /// 缓存依赖
            /// </summary>
            public CacheDependency Dependency { get; set; }

            /// <summary>
            /// 命中次数，命中次数少的使用得少，优先移去缓存
            /// </summary>
            public int Hits { get; set; }

            public CacheBag(TDKey key, TData value,CacheDependency dependency)
            {
                Hits = 0;
                Key = key;
                Value = value;
                Dependency = dependency;
                CreateTime = DateTime.Now;
                AccessTime = CreateTime;
            }

        }

        #endregion

    }


}